package com.qipay.pay.impl;

import com.qipay.baas.model.ChannelType;
import com.qipay.baas.model.CpChannel;
import com.qipay.baas.model.Order;
import com.qipay.channelType.ChannelTypeService;
import com.qipay.core.help.DicHelper;
import com.qipay.cp.CpService;
import com.qipay.cpchannel.service.CpChannelService;
import com.qipay.order.config.OrderSettleStateEnum;
import com.qipay.order.config.OrderStateEnum;
import com.qipay.order.service.OrderService;
import com.qipay.pay.FourPayService;
import com.qipay.pay.entity.CpPayAlipayWapRequest;
import com.qipay.scalar.type.ApiStatusEnum;
import com.qipay.scalar.type.ChanelTypeTypeEnum;
import com.qipay.security.sign.service.SignService;
import com.qipay.sp.CpSpChannelService;
import com.qipay.sp.SpConfigService;
import com.qipay.sp.SpService;
import com.qipay.sp.excutor.ExcutorFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.math.BigDecimal;
import java.util.*;

@Service
public class FourPayServiceImpl implements FourPayService {

    protected final Log logger = LogFactory.getLog(this.getClass());

    private final static String CONFIG_JS_NAME = "is_raw";

    @Autowired
    OrderService orderService;

    @Autowired
    CpChannelService cpChannelService;

    @Autowired
    SpService spService;

    @Autowired
    SpConfigService spConfigService;

    @Autowired
    SignService signService;
    @Autowired
    CpSpChannelService cpSpChannelService;
    @Autowired
    CpService cpService;
    @Autowired
    ChannelTypeService channelTypeService;
    @Value("${api.formPay.url}")
    private String formPayUrl;

    @Transactional
    @Override
    public Map<String, Object> wapPayCreateOrder(CpPayAlipayWapRequest entity) throws Exception {
        //校验参数
        CpChannel cpChannel = checkParams(entity);
        //组装订单
        Order order = generateOrder(entity,cpChannel);
        //筛选指定的通道
        List<Long>spIds = selectSpIds(Integer.valueOf(entity.getMchId()),order.getChannelType());
        //通道轮询
        order = orderService.selectASp(order, entity.getTradeType().byteValue(),spIds);
        //录入订单
        orderService.insert(order);
        if (order.getId() == null || order.getId() == 0) {
            logger.error("订单信息无法入库，order.id:" + order.getId());
            throw new Exception("无法生成订单");
        }
        //执行工厂方法
        Map<String, Object> objectMap = new HashMap<>();objectMap.put("cpSecretId",entity.getCpSecretId());
        Map<String, Object> result = excuteResult(order,objectMap);
        //考虑到有的通道会死掉的情况，所以这里判断下降低通道死亡对成功率的影响
        if(result == null|| !ApiStatusEnum.OK.getCode().equals(result.get("status"))) {
            order = orderService.selectASp(order, order.getChannelType(),spIds);
            orderService.update(order);
            result = excuteResult(order,objectMap);
        }
        return result;
    }

    private List<Long> selectSpIds(Integer cpId,Byte channelType){
        List<Long>spIds = cpSpChannelService.selectSpIds(cpId, channelType);
        Assert.isTrue(spIds.size()>0,"找不到可用的通道:未指定通道");
        return spIds;
    }
    /**
     * 组装订单
     * @param entity
     * @return
     */
    private Order generateOrder(CpPayAlipayWapRequest entity,CpChannel cpChannel){
        Long cpId = Long.valueOf(entity.getMchId());
        Order order = orderService.getByCpidAndOutTradeNo(cpId, entity.getOutTradeNo());
        if (order != null) {
            Assert.isTrue(Objects.equals(OrderStateEnum.NOTPAY.getType(), order.getState()),
                    "无效的订单状态:" + OrderStateEnum.getByType(order.getState()).getInfo());
        } else {
            order = new Order();
            order.setCpId(cpId);
            order.setChannelType(entity.getTradeType().byteValue());
            order.setBody(entity.getBody());
            order.setAmount(entity.getTotalFee());
            order.setFeeRecharge(cpChannel.getFeeRecharge());
            order.setState(OrderStateEnum.NOTPAY.getType());
            order.setTradeNo(orderService.createOrder(18));
            order.setOutTradeNo(entity.getOutTradeNo());
            order.setNotifyUrl(entity.getNotifyUrl());
            order.setCreateTime(new Date());
            order.setSettleStatus(OrderSettleStateEnum.NOTSETTLE.getType());
            order.setRemark(entity.getMchCreateIp());

            order.setActualPayAmount(order.getAmount());
            order.setFee(cpChannel.getFeeRecharge().multiply(new BigDecimal(entity.getTotalFee().toString())).intValue());
            order.setActualAmount(order.getAmount() - order.getFee());
            //查看代理的channel，为了分账
            CpChannel agentChannel = cpChannelService.getAgentChannel(cpChannel);
            if (agentChannel != null) {
                order.setAgentId(agentChannel.getCpId());
                order.setAgentFeeRecharge(agentChannel.getFeeRecharge());
                order.setAgentFee(agentChannel.getFeeRecharge().multiply(
                        new BigDecimal(entity.getTotalFee().toString())
                ).intValue());
            }
        }
        return order;
    }
    /**
     * 校验参数
     * @param entity
     */
    private CpChannel checkParams(CpPayAlipayWapRequest entity){
        Assert.notNull(entity, "缺少交易信息");
        Assert.notNull(entity.getBody(), "缺少参数body");
        Assert.notNull(entity.getMchId(), "缺少参数mch_id");
        Assert.notNull(entity.getTradeType(), "缺少参数trade_type");
        Assert.notNull(entity.getOutTradeNo(), "缺少参数out_trade_no");
        Assert.notNull(entity.getTotalFee(), "缺少参数total_fee");
        Assert.notNull(entity.getMchCreateIp(), "缺少参数mch_create_ip");
        Assert.notNull(entity.getNotifyUrl(), "缺少参数notify_url");

        CpChannel cpChannel = cpChannelService.getByCpIdAndChannelType(Long.valueOf(entity.getMchId()),
                entity.getTradeType().byteValue());
        Assert.isTrue(cpChannel!=null&&cpChannel.getState() == 0, "该商户没有开通支付渠道:"+entity.getTradeType());
        Assert.notNull(cpChannel.getFeeRecharge(), "该商户没有配置费率");
        return cpChannel;

    }
    /**
     * 处理创建订单之后的执行器
     * @param order
     * @return
     * @throws Exception
     */
    private Map<String, Object> excuteResult(Order order,Map<String,Object>objectMap) throws Exception {
        Map<String, Object> result = new HashMap<>();
        String isRaw = spConfigService.getValue(order.getSpId(), FourPayServiceImpl.CONFIG_JS_NAME);

        if("1".equals(isRaw)) {
            String mchId = order.getCpId().toString();
            String tradeNo = order.getTradeNo();
            Map<String, Object> params = new HashMap<>();
            params.put("mch_id", mchId);
            params.put("trade_no", tradeNo);
            params.put("type", order.getChannelType());
            String mySig = signService.getSign(params, objectMap.get("cpSecretId").toString(), "sign");
            params.put("sign", mySig);

            String redirectUrl = formPayUrl;
            redirectUrl += "?mch_id=" + mchId + "&trade_no=" + tradeNo+ "&type="+order.getChannelType()
                    + "&sign=" + mySig;
            result.put("pay_info", redirectUrl);
            result.put("status", ApiStatusEnum.OK.getCode());
        }else{
            result = excuteFactoryExcutor(order);
        }
        return result;
    }


    /**
     * 需求：针对form直接提交到上游支付页面的通道开发
     * 作用：供下游二次重定向
     * @param orderId
     * @return
     * @throws Exception
     */
    @Transactional
    @Override
    public Map<String, Object> wapPay(Long orderId) throws Exception {
        Assert.notNull(orderId, "请输入交易信息");

        Order order = orderService.get(orderId);
        Assert.notNull(order, "请输入交易信息");
        Assert.isTrue(Objects.equals(OrderStateEnum.NOTPAY.getType(), order.getState()),
                "无效的订单状态:" + OrderStateEnum.getByType(order.getState()).getInfo());

        return excuteFactoryExcutor(order);
    }

    /**
     * channelType的RateAdvance字段表示channelType的类型，0表示扫码，1表示wap
     * @param order
     * @return
     */
    private Map<String, Object> excuteFactoryExcutor(Order order) {
        Map<String, Object> result = null;
        try {
            ExcutorFactory excutorFactory = spService.getFactory(order.getSpId());
            ChannelType channelType = channelTypeService.findById(order.getChannelType());
            if (channelType.getRateAdvance().intValue()== ChanelTypeTypeEnum.SCAN_CODE.getCode())
                result = excutorFactory.createNativeExcutor().excutor(order);
            else result = excutorFactory.createWapExcutor().excutor(order);

        }catch (Exception ex) {
            ex.printStackTrace();
            logger.error(order.getTradeNo() + ":" + order.getChannelType().toString()
                    + " -- 执行工厂方法出错:" + ex.toString());
        }
        return result;
    }

    @Override
    public Map<String, Object> testCreateOrder(CpPayAlipayWapRequest entity) throws Exception {
        //校验参数
        CpChannel cpChannel = checkParams(entity);
        //组装订单
        Order order = generateOrder(entity,cpChannel);
        //筛选指定的通道:测试通道
        List<Long>spIds = new ArrayList<>();
        spIds.add(entity.getSpId());
        //通道轮询
        order = orderService.selectASp(order, entity.getTradeType().byteValue(),spIds);
        //录入订单
        orderService.insert(order);
        if (order.getId() == null || order.getId() == 0) {
            logger.error("订单信息无法入库，order.id:" + order.getId());
            throw new Exception("无法生成订单");
        }
        //执行工厂方法
        Map<String, Object> objectMap = new HashMap<>();objectMap.put("cpSecretId",entity.getCpSecretId());
        Map<String, Object> result = excuteResult(order,objectMap);
        //考虑到有的通道会死掉的情况，所以这里判断下降低通道死亡对成功率的影响
        if(result == null|| !ApiStatusEnum.OK.getCode().equals(result.get("status"))) {
            order = orderService.selectASp(order, order.getChannelType(),spIds);
            orderService.update(order);
            result = excuteResult(order,objectMap);
        }
        return result;
    }

}
